热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

池子比率:BSV区块链上的去中心化金融应用——Uniswap分析

篇首语:本文由编程笔记#小编为大家整理,主要介绍了BSV 区块链上的 DeFi :Uniswap相关的知识,希望对你有一定的参考价值。 Uniswap Uniswap 是所谓的 Decentrali

篇首语:本文由编程笔记#小编为大家整理,主要介绍了BSV 区块链上的 DeFi :Uniswap相关的知识,希望对你有一定的参考价值。



Uniswap

Uniswap 是所谓的 Decentralized Exchange,它允许个人或称为流动性提供者,将 Token 汇集到智能合约中提供流动性。


概述

我们实现了 Uniswap V1,它只在 BSV 和 Token 之间直接交换。如@state 装饰器所示,我们使用一个带有 @state 装饰器 的有状态合约来表示池子。它包含两个 Token :一个用于我们正在交换的 Token (第 7 行),另一个是治理 Token (第 11 行),称为流动性池(LP) Token 。该池将 BSV 直接存储在 UTXO 中(以 satoshis 为单位),将 Token 存储在对应的公钥哈希下(第 3 行)。

contract Uniswap
// pool's public key
PubKey poolPubkey;
// the main token
@state
ERC20 token;
// the liquidity pool governance token
@state
ERC20 lpToken;
...



Uniswap 合约源代码

增加流动性

任何人都可以通过调用函数 addLiquidity 向池中添加流动性。有两种情况(在第 9 行检查):


  1. 首次增加流动性:可以存入任意数量的 BSV 和 Token 。
  2. 添加更多流动性: BSV 和 Token 存入的比率必须与池中的现有比率相匹配(第 22 行)。

// add bsv and token to liquidity pool
public function addLiquidity(PubKey sender, Sig senderSig, int tokenAmount, int senderBalance, int senderKeyIndex, int oldTokenBalance,
int lpSenderBalance, int lpSenderKeyIndex, int newBsvBalance, SigHashPreimage txPreimage
)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);

// mint new lp tokens for the liquidity provider
if (oldBsvBalance == 0)
// initialize pool

// initially, just mint new lp tokens per the amount of new bsvs deposited
int lpMint = newBsvBalance;
require(this.lpToken.mint(sender, lpSenderBalance, lpMint, lpSenderKeyIndex));
else
// add more liquidity
int bsvAmount = newBsvBalance - oldBsvBalance;

// deposit ratio must be the same with current pool ration
// i.e., oldBsvBalance / oldTokenBalance == bsvAmount / tokenAmount
require(oldBsvBalance * tokenAmount == bsvAmount * oldTokenBalance);
// mint new lp tokens, proportinal to the amount of new bsvs deposited
int lpMint = this.lpToken.totalSupply() * bsvAmount / oldBsvBalance;
require(this.lpToken.mint(sender, lpSenderBalance, lpMint, lpSenderKeyIndex));

// transfer tokens to the pool
require(this.token.transferFrom(sender, this.poolPubkey, tokenAmount, senderBalance, senderKeyIndex, oldTokenBalance, senderKeyIndex));

require(this.propagateState(newBsvBalance, txPreimage));

存入 BSV 后,新的 LP Token 在第 26 行按比例铸造给流动性提供者。 Token 在第 30 行转移到池子对应的账户。

例如,如果池中有 10 个 BSV 和 100 个 LP Token ,而 Alice 又向其中存入了 5 个 BSV ,则将向她铸造 50 个新的 LP Token 。


移除流动性

流动性提供者调用函数 removeLiquidity 来提取他们的资金,包括 BSV 和 Token 。

// remove bsv and token from liquidity pool
public function removeLiquidity(PubKey sender, int lpAmount, Sig senderSig, int oldTokenBalance, int senderKeyIndex, int senderBalance,
int lpSenderBalance, int lpSenderKeyIndex, SigHashPreimage txPreimage
)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);

// withdraw amount
int bsvAmount = oldBsvBalance * lpAmount / this.lpToken.totalSupply();
int tokenAmount = oldTokenBalance * lpAmount / this.lpToken.totalSupply();
// burn the lp tokens
require(this.lpToken.burn(sender, lpSenderBalance, lpAmount, lpSenderKeyIndex));
// transfer tokens from pool to the sender
require(this.token.transferFrom(this.poolPubkey, sender, tokenAmount, oldTokenBalance, senderKeyIndex, senderBalance, senderKeyIndex));
// transfer bsvs to the sender
int newBsvBalance = oldBsvBalance - bsvAmount;

require(this.propagateState(newBsvBalance, txPreimage));

流动性提供者拥有的 LP Token 的数量来提取流动性(第 9 行和第 10 行)。提款后,LP Token 在第 13 行被烧毁。第 16 行将 Token 从池子中转移到流动性提供者。第 18 行和第 20 行对 BSV 做同样的事情。

请注意,除了合约输出之外,还需要在同一交易中的另一个输出将 BSV 返回给流动性提供者。


BSV -> Token

用户调用函数 swapBsvToToken 将 BSV 兑换成 Token 。在池子收到 BSV 后(在第 7 行计算),第 10 行计算要返回的 Token 数量,第 13 行将它们返回给用户。

// swap bsvs for tokens
public function swapBsvToToken(PubKey sender, int tokenAmount, Sig senderSig, int oldTokenBalance, int senderKeyIndex, int senderBalance,
int newBsvBalance, SigHashPreimage txPreimage
)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);
int bsvAmount = newBsvBalance - oldBsvBalance;
// calculate tokens in return
int tokensAmount = this.getAmount(bsvAmount, oldBsvBalance, oldTokenBalance);
// transfer tokens from pool to the sender
require(this.token.transferFrom(this.poolPubkey, sender, tokensAmount, oldTokenBalance, senderKeyIndex, senderBalance, senderKeyIndex));
require(this.propagateState(newBsvBalance, txPreimage));


Token -> BSV

用户调用函数 swapTokenToBsv 将 Token 兑换成 BSV 。第 9 行计算要返回的 BSV 数量。第 13 行将 Token 转移到池子中。

// swap tokens for bsvs
public function swapTokenToBsv(PubKey sender, int tokenAmount, Sig senderSig, int senderBalance, int senderKeyIndex, int oldTokenBalance,
int lpSenderBalance, SigHashPreimage txPreimage
)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);
// calculate bsvs in return
int bsvsAmount = this.getAmount(tokenAmount, oldTokenBalance, oldBsvBalance);
int newBsvBalance = oldBsvBalance - bsvsAmount;
// transfer tokens to the pool
require(this.token.transferFrom(sender, this.poolPubkey, tokenAmount, senderBalance, senderKeyIndex, oldTokenBalance, senderKeyIndex));

require(this.propagateState(newBsvBalance, txPreimage));

removeLiquidity 类似,需要另一个输出来将 BSV 返回给用户。


讨论

我们已经演示了如何在 BSV 区块链上实现一个基本的类似 Uniswap 的合约。有很多方法可以扩展它以使其更实用。


  • 价格公式:我们使用以下代码来确定价格,仅基于 BSV 和 Token 储备。它被称为恒定和公式,可能导致池子被排空。为了避免耗尽,可以使用更复杂的公式,如恒定乘积公式 (x * y = k),如 Uniswap 中。

// use reserve ratio as price
function getAmount(int input, int inputReserve, int outputReserve) : int
return outputReserve * input / inputReserve;


  • 流动性挖矿:我们可以对每次交换收取费用,并用这些费用来奖励流动性提供者。
  • 允许用户直接将一个 Token 换成另一个 Token 。

TokenSwap 实际上已经实现了以上所有以及更多。


致谢

本作品灵感来源于陈诚的这篇文章。


推荐阅读
  • 利用ViewComponents在Asp.Net Core中构建高效分页组件
    通过运用 ViewComponents 技术,在 Asp.Net Core 中实现了高效的分页组件开发。本文详细介绍了如何通过创建 `PaginationViewComponent` 类并利用 `HelloWorld.DataContext` 上下文,实现对分页参数的定义与管理,从而提升 Web 应用程序的性能和用户体验。 ... [详细]
  • 深入解析:JavaScript中的表达式与语句有何不同
    深入解析:JavaScript中的表达式与语句有何不同 ... [详细]
  • 计算 n 叉树中各节点子树的叶节点数量分析 ... [详细]
  • BZOJ4240 Gym 102082G:贪心算法与树状数组的综合应用
    BZOJ4240 Gym 102082G 题目 "有趣的家庭菜园" 结合了贪心算法和树状数组的应用,旨在解决在有限时间和内存限制下高效处理复杂数据结构的问题。通过巧妙地运用贪心策略和树状数组,该题目能够在 10 秒的时间限制和 256MB 的内存限制内,有效处理大量输入数据,实现高性能的解决方案。提交次数为 756 次,成功解决次数为 349 次,体现了该题目的挑战性和实际应用价值。 ... [详细]
  • 在Java中,匿名函数作为一种无名的函数结构,无法独立调用;而在JavaScript中,不仅有类似的匿名函数,还有立即执行函数(IIFE)和闭包等高级特性。立即执行函数同样基于匿名函数实现,但会在定义时立即执行,而闭包则通过嵌套函数来捕获外部变量,实现数据封装和持久化。这些不同的函数形式在实际开发中各有应用场景,理解其特点有助于更好地利用语言特性进行编程。 ... [详细]
  • 如何将PHP文件上传至服务器及正确配置服务器地址 ... [详细]
  • 本文详细介绍了如何在Linux系统中搭建51单片机的开发与编程环境,重点讲解了使用Makefile进行项目管理的方法。首先,文章指导读者安装SDCC(Small Device C Compiler),这是一个专为小型设备设计的C语言编译器,适合用于51单片机的开发。随后,通过具体的实例演示了如何配置Makefile文件,以实现代码的自动化编译与链接过程,从而提高开发效率。此外,还提供了常见问题的解决方案及优化建议,帮助开发者快速上手并解决实际开发中可能遇到的技术难题。 ... [详细]
  • 如何在 Python 编程中实现各种数据类型的字符串转换? ... [详细]
  • 在Python编程中,掌握高级技巧对于提升代码效率和可读性至关重要。本文重点探讨了生成器和迭代器的应用,这两种工具不仅能够优化内存使用,还能简化复杂数据处理流程。生成器通过按需生成数据,避免了大量数据加载对内存的占用,而迭代器则提供了一种优雅的方式来遍历集合对象。此外,文章还深入解析了这些高级特性的实际应用场景,帮助读者更好地理解和运用这些技术。 ... [详细]
  • 从 Java 过渡到 Ruby,不仅是一次编程语言的转换,更是一段技术进阶的旅程。本文将深入探讨两种语言在语法、生态系统和开发模式上的差异,帮助开发者顺利实现转型,并在新的环境中高效地编写高质量代码。 ... [详细]
  • 利用 Python 实现 Facebook 账号登录功能 ... [详细]
  • 探索JavaScript倒计时功能的三种高效实现方法及代码示例 ... [详细]
  • 在Laravel中实现PHP对JSON数据的发布与处理 ... [详细]
  • PyQt5 QTextEdit:深入解析Python中多功能GUI库的应用与实现
    本文详细探讨了 PyQt5 中 QTextEdit 组件在 Python 多功能 GUI 库中的应用与实现。PyQt5 是 Qt 框架的 Python 绑定,提供了超过 620 个类和 6000 个函数及方法,广泛应用于跨平台应用程序开发。QTextEdit 作为其中的重要组件,支持丰富的文本编辑功能,如富文本格式、文本高亮和自定义样式等。PyQt5 的流行性不仅在于其强大的功能,还在于其易用性和灵活性,使其成为开发复杂用户界面的理想选择。 ... [详细]
  • 深入解析Spring框架中的双亲委派机制突破方法
    在探讨Spring框架中突破双亲委派机制的方法之前,首先需要了解类加载器的基本概念。类加载器负责将类的全限定名转换为对应的二进制字节流。每个类在被特定的类加载器加载后,其唯一性得到保证。然而,这种机制在某些场景下可能会限制灵活性,因此Spring框架提供了一些策略来突破这一限制,以实现更加动态和灵活的类加载。这些策略不仅能够提升系统的可扩展性,还能在复杂的运行环境中确保类的正确加载和管理。 ... [详细]
author-avatar
爱旅游的星琴
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有